﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace LCH
{
    partial class LchMeshPainter
    {
        private class TerrainTools
        {
            //public Terrain editorTerrain;
            public TerrainData terrainData = null;
            public float terrainQuality = 0.25f;
            public int face = 5000;
            public float baseMeshCount = 0;
            public Mesh baseMesh;
            public Mesh lodMesh;
      
            public GameObject g;
            public int GetConvertedMeshPolyCount()
            {
                if (null == terrainData)
                    return 0;
                TerrainData terrain =  terrainData;
                int w = terrain.heightmapResolution;
                int h = terrain.heightmapResolution;

                float tRes = 1f / terrainQuality;
                w = (int)((w - 1) / tRes + 1);
                h = (int)((h - 1) / tRes + 1);

                return (w - 1) * (h - 1) * 2;
            }
            public void CreateLodMesh(int type)
            {
                if (null != lodMesh)
                    GameObject.DestroyImmediate(lodMesh);
 
                switch (type)
                {
                    case 0:
                        {
                            BuildLodMesh2(face);
                        }
                        break;
                    case 1:
                        {
                            lodMesh = (Mesh)UnityEngine.Object.Instantiate(baseMesh);
                            lodMesh.name = baseMesh.name;
                            BuildLodMesh(lodMesh, face);

                            
                        }
                        break;
                }

                var vs = lodMesh.vertices;
                Vector3 size = terrainData.size;

                float[,,] alphaMapData = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight);
                int maxSize = terrainData.heightmapResolution - 1;
                for (int i = 0; i < vs.Length; i++)
                {
                    var v = vs[i];


                    int x = (int)(terrainData.heightmapResolution *  v.x / size.x);//在高度图中的坐标
                    int y = (int)(terrainData.heightmapResolution * v.z / size.x);

                    float h = terrainData.GetHeight(maxSize-x, y);
                    v.y = h  ;
                }

                lodMesh.vertices = vs;
                lodMesh.RecalculateNormals();
                RecalculateSmoothNormals(lodMesh);




            }

            public   void RecalculateSmoothNormals(Mesh mesh)
            {
                Vector3[] vertices = mesh.vertices;
                int[] triangles = mesh.triangles;

                Vector3[] normals = new Vector3[vertices.Length];
                int triangleCount = triangles.Length / 3;

                // 计算每个三角形的面法线
                for (int i = 0; i < triangleCount; i++)
                {
                    int i0 = triangles[i * 3 + 0];
                    int i1 = triangles[i * 3 + 1];
                    int i2 = triangles[i * 3 + 2];

                    Vector3 v0 = vertices[i0];
                    Vector3 v1 = vertices[i1];
                    Vector3 v2 = vertices[i2];

                    Vector3 faceNormal = Vector3.Cross(v1 - v0, v2 - v0).normalized;

                    // 将面法线加到顶点上
                    normals[i0] += faceNormal;
                    normals[i1] += faceNormal;
                    normals[i2] += faceNormal;
                }

                // 对每个顶点法线进行归一化
                for (int i = 0; i < normals.Length; i++)
                {
                    normals[i] = normals[i].normalized;
                }

                mesh.normals = normals;

                mesh.RecalculateBounds();
                mesh.UploadMeshData(false); // 或 true，根据需求
            }
            void BuildLodMesh2(int fase)
            {
                var meshSimplifier = new UnityMeshSimplifier.MeshSimplifier();
                meshSimplifier.Initialize(baseMesh);

                float quality = ((float)face) / baseMeshCount;



                var op = meshSimplifier.SimplificationOptions;
                op.PreserveBorderEdges = true;
                meshSimplifier.SimplificationOptions = op;
                meshSimplifier.SimplifyMesh(quality);
                
                // Create our final mesh and apply it back to our mesh filter
                lodMesh = meshSimplifier.ToMesh();
            }
            Vector3 meshScale;
            Vector2 uvScale;
            int w;
            int h;

            public Mesh BakeMesh(int simple = 0)
            {
                int vertexCountScale = 10;

                switch (simple)
                {
                    case 1:
                        {
                            vertexCountScale = 12;
                        }
                        break;
                    case 2:
                        {
                            vertexCountScale = 8;
                        }
                        break;
                    case 3:
                        {
                            vertexCountScale = 4;
                        }
                        break;
                    case 100:
                        {
                            vertexCountScale = 6;
                        }
                        break;

                }
                 
                w = terrainData.heightmapResolution;
                h = terrainData.heightmapResolution;

                

                Vector3 size = terrainData.size;
                float[,,] alphaMapData = terrainData.GetAlphamaps(0, 0, terrainData.alphamapWidth, terrainData.alphamapHeight);
                meshScale = new Vector3(size.x / (w - 1f) * vertexCountScale, 1, size.z / (h - 1f) * vertexCountScale);


                w = (w - 1) / vertexCountScale + 1;
                h = (h - 1) / vertexCountScale + 1;
                 

                uvScale = new Vector2(1f / (w - 1f), 1f / (h - 1f));
                Vector3[] vertices = new Vector3[w * h];
                Vector2[] uvs = new Vector2[w * h];
   
                Vector3 offset = new Vector3(-size.x,0, 0);
                int maxSize = terrainData.heightmapResolution - 1;
                // 顶点，uv，每个顶点每个图片所占比重
                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        int index = j * w + i;
                        float z = terrainData.GetHeight(maxSize - i * vertexCountScale, j * vertexCountScale);
                        vertices[index] = Vector3.Scale(new Vector3(i, z, j), meshScale)+ offset;
                        Vector2 _uv = Vector2.Scale(new Vector2(i, j), uvScale);
                        _uv.x = 1.0f - _uv.x;
                        uvs[index] = _uv;

                        // alpha map
                        int i2 = (int)(i * terrainData.alphamapWidth / (w - 1f));
                        int j2 = (int)(j * terrainData.alphamapHeight / (h - 1f));
                        i2 = Mathf.Min(terrainData.alphamapWidth - 1, i2);
                        j2 = Mathf.Min(terrainData.alphamapHeight - 1, j2);
                        var alpha0 = alphaMapData[j2, i2, 0];
                        var alpha1 = alphaMapData[j2, i2, 1];
                        var alpha2 = alphaMapData[j2, i2, 2];
                        var alpha3 = alphaMapData[j2, i2, 3];
                        //alphasWeight[index] = new Vector4(alpha0, alpha1, alpha2, alpha3);
                    }
                }


                int[] triangles = new int[(w - 1) * (h - 1) * 6];
                int triangleIndex = 0;
                for (int i = 0; i < w - 1; i++)
                {
                    for (int j = 0; j < h - 1; j++)
                    {
                        int a = j * w + i;
                        int b = (j + 1) * w + i;
                        int c = (j + 1) * w + i + 1;
                        int d = j * w + i + 1;

                        triangles[triangleIndex++] = a;
                        triangles[triangleIndex++] = b;
                        triangles[triangleIndex++] = c;

                        triangles[triangleIndex++] = a;
                        triangles[triangleIndex++] = c;
                        triangles[triangleIndex++] = d;
                    }
                }

                Mesh mesh = new Mesh();
                mesh.vertices = vertices;
                mesh.uv = uvs;
                mesh.triangles = triangles;
                //mesh.tangents = alphasWeight;
                mesh.RecalculateNormals();
                return mesh;
            }
            public void MeskMeshSimple()
            {
                if (null != baseMesh)
                {
                    GameObject.DestroyImmediate(baseMesh, true);
                    baseMesh = null;
                }


                baseMesh = BakeMesh(100);
                baseMeshCount = baseMesh.triangles.Length / 3;
            }
            public void MakeMesh()
            {
                if (null != baseMesh)
                {
                    GameObject.DestroyImmediate(baseMesh, true);
                    baseMesh = null;
                }
            
                
                baseMesh = BakeMesh(); 
                baseMeshCount = baseMesh.triangles.Length / 3;
            }
            public static bool ModifyTextureFormatPC(string path)
            {
                TextureImporterPlatformSettings textureSettings = new TextureImporterPlatformSettings();
                try
                {
                    TextureImporter ti = (TextureImporter)TextureImporter.GetAtPath(path);
                    textureSettings = ti.GetPlatformTextureSettings("Standalone");
                    textureSettings.overridden = true;
                    textureSettings.format = TextureImporterFormat.RGBA32;
                    textureSettings.androidETC2FallbackOverride = AndroidETC2FallbackOverride.Quality32BitDownscaled;
                    ti.SetPlatformTextureSettings(textureSettings);
                    AssetDatabase.SaveAssets();
                    AssetDatabase.ImportAsset(path);
                    return true;
                }
                catch (System.Exception e)
                {
                    Debug.LogError(e.ToString() + " " + textureSettings.format + "" + path + "\n" + e.StackTrace);
                }
                return false;
            }
            public  bool ModifyTextureFormat(string path, string platformString = "Android")
            {
                TextureImporterPlatformSettings textureSettings = new TextureImporterPlatformSettings();
                try
                {
                    TextureImporter ti = (TextureImporter)TextureImporter.GetAtPath(path);
                    ti.sRGBTexture = false;
                    textureSettings = ti.GetPlatformTextureSettings(platformString);

                    textureSettings.overridden = true;
                    textureSettings.format = TextureImporterFormat.ASTC_4x4;

                    textureSettings.androidETC2FallbackOverride = AndroidETC2FallbackOverride.Quality32BitDownscaled;
                    ti.SetPlatformTextureSettings(textureSettings);
                    AssetDatabase.SaveAssets();
                    AssetDatabase.ImportAsset(path);
 
                    return true;


                }
                catch (System.Exception e)
                {
                    Debug.LogError(e.ToString() + " " + textureSettings.format + "" + path + "\n" + e.StackTrace);
                }
                return false;
            }

            string[] LAYER_KEY_WORLD = new string[] { "_LMPLAY_L3", "_LMPLAY_L4", "_LMPLAY_L5", "_LMPLAY_L6", "_LMPLAY_L7", "_LMPLAY_L8" };

            void SetLayerKeyWorld(Material mat,int index)
            {
                index = Mathf.Min(LAYER_KEY_WORLD.Length - 1, Mathf.Max(index - 3, 0) );
                for (int i = 0; i < LAYER_KEY_WORLD.Length; i++)
                {
                    if (index == i)
                    {
                        mat.EnableKeyword(LAYER_KEY_WORLD[i]);
                    }
                    else
                    {
                        mat.DisableKeyword(LAYER_KEY_WORLD[i]);
                    }
                }
            }


            public void Save(string path, int lod = 0 )
            {
                
                string mesh_path = path;

                if (lod > 0)
                {
                    lodMesh = BakeMesh(lod);
                    lodMesh.RecalculateNormals();
                    
                }
                /*else
                {
                    Vector3 _max = Vector3.Scale(new Vector3(w - 1, 0, h - 1), meshScale);
                    var vs = lodMesh.vertices;
                    Vector2[] uvs = new Vector2[vs.Length];
                    for (int i = 0; i < vs.Length; i++)
                    {
                        uvs[i] = new Vector2(vs[i].x / _max.x, vs[i].z / _max.z);
                    }

                    lodMesh.uv = uvs;
                }*/
                RecalculateSmoothNormals(lodMesh);
                //Vector3 meshScale;
                //int w;
                //int h;

                SaveAsset(mesh_path, lodMesh);


                Mesh _mesh = AssetDatabase.LoadAssetAtPath<Mesh>(mesh_path);
                TerrainData terrain = terrainData;
                Texture2D[] textures = terrain.alphamapTextures;

                Shader _shader = Shader.Find(setting.defaultTerrainShaderName);
                if (null == _shader)
                {
                    EditorUtility.DisplayDialog(Languages.GetValue(54, "Error"), "Shader " + setting.defaultTerrainShaderName + Languages.GetValue(55, "Not Found!!! You can change the default shader name on setting panel."), "Ok");
                }
                Material mat = new Material(_shader);
                
                for (int i = 0; i < textures.Length; i++)
                {
                    Texture2D t = textures[i];
                    byte[] date = LchEncode.EncodeToTGA(t);
                    string savePath = path + t.name+ ".tga";
                    System.IO.File.WriteAllBytes(savePath, date);
                    System.IO.File.WriteAllBytes(savePath, t.EncodeToPNG());
                    AssetDatabase.ImportAsset(savePath);
                    ModifyTextureFormat(savePath, "Android");
                    ModifyTextureFormat(savePath, "iPhone");
                    ModifyTextureFormatPC(savePath);
                    t = AssetDatabase.LoadAssetAtPath<Texture2D>(savePath);
                    if (i == 0)
                    {
                        mat.SetTexture(setting.ctrlTextureName1, t);
                    }
                    else if (i == 1)
                    {
                        mat.SetTexture(setting.ctrlTextureName2, t);
                    }
                }


                
                var terrainLayers = terrain.terrainLayers;
                int layLen =  terrainLayers.Length;
                for (int i = 0; i < terrainLayers.Length; i++)
                {
                    var tl = terrainLayers[i];
                    mat.SetTexture(setting.prefix + i, tl.diffuseTexture);
                    mat.SetTexture(setting.norPrefix + i, tl.normalMapTexture);
                    Vector2 scale = new Vector2( terrain.baseMapResolution/ tl.tileSize.x,  terrain.baseMapResolution/ tl.tileSize.y);
                    mat.SetTextureScale(setting.prefix + i  , scale);
                }

                
                SetLayerKeyWorld(mat, layLen );
                 

                string mat_path = path + "terrain.mat";
                AssetDatabase.CreateAsset(mat,mat_path);
                

                mat = (Material)AssetDatabase.LoadAssetAtPath<Material>(mat_path);
                GameObject g = new GameObject("Lch Terrain");
                g.AddComponent<MeshFilter>().sharedMesh = _mesh;
                g.AddComponent<MeshRenderer>().sharedMaterial = mat;

                string go_path = path + "terrainobj.prefab";

                Object prefab = PrefabUtility.CreateEmptyPrefab(go_path);
                PrefabUtility.ReplacePrefab(g, prefab, ReplacePrefabOptions.ConnectToPrefab);
              
            }

            public void  CheckPerviewObject()
            {
                if (null == g)
                {
                    g = new GameObject();
                    g.hideFlags = HideFlags.HideAndDontSave;
                    g.SetActive(false);
                    g.AddComponent<MeshFilter>();
                    var r = g.AddComponent<MeshRenderer>();
                     
                }
                g.GetComponent<MeshFilter>().sharedMesh = lodMesh;
            }

            void SaveAsset(string filePath,  Mesh mesh)
            {

                using (System.IO.StreamWriter writer = new System.IO.StreamWriter(filePath))
                {
                    // 写入顶点信息
                    foreach (Vector3 vertex in mesh.vertices)
                    {
                        writer.WriteLine($"v {vertex.x} {vertex.y} {vertex.z}");
                    }

                    // 写入法线信息
                    foreach (Vector3 normal in mesh.normals)
                    {
                        writer.WriteLine($"vn {normal.x} {normal.y} {normal.z}");
                    }

                    // 写入纹理坐标信息
                    foreach (Vector2 uv in mesh.uv)
                    {
                        writer.WriteLine($"vt {uv.x} {uv.y}");
                    }

                    // 写入面信息
                    int[] triangles = mesh.triangles;
                    for (int i = 0; i < triangles.Length; i += 3)
                    {
                        int v1 = triangles[i] + 1;
                        int v2 = triangles[i + 1] + 1;
                        int v3 = triangles[i + 2] + 1;
                        writer.WriteLine($"f {v1}/{v1}/{v1} {v2}/{v2}/{v2} {v3}/{v3}/{v3}");
                    }
                }

                Debug.Log($"Mesh saved to {filePath}");
                 
                AssetDatabase.ImportAsset(filePath);
            }
            public void Release()
            {
                if (null != baseMesh)
                    GameObject.DestroyImmediate(baseMesh, true);
            }

        }

    }

}
